#include "fxcc/core/graph/common/GlslShader.h"

using namespace fxcc::graph::common;

GlslShader::Type::Type(const glslang::TType* type)
{
    m_BasicType = type->getBasicType();
    m_VectorSize = type->getVectorSize();
    m_Sampler = type->getSampler();
    m_Qualifier = type->getQualifier();


}


void GlslShader::UniformVariable::Influence(fxcc::graph::common::MaterialView& material) const
{
    switch (m_Type.m_BasicType)
    {
    case glslang::EbtInt:
        material.m_Params.InitInt(m_Name, m_Type.m_VectorSize);
        break;
    case glslang::EbtFloat:
        material.m_Params.InitFloat(m_Name, m_Type.m_VectorSize);
        break;
    case glslang::EbtSampler:
        if (m_Type.m_Sampler.dim == glslang::TSamplerDim::Esd2D)
        {
            if (m_Type.m_Sampler.isArrayed())
            {
                material.m_Textures.InitTexture2DArray(m_Name);
            }
            else
            {
                material.m_Textures.InitTexture2D(m_Name);
            }
        }
        if (m_Type.m_Sampler.dim == glslang::TSamplerDim::EsdCube)
        {
            if (m_Type.m_Sampler.isArrayed())
            {
                material.m_Textures.InitTextureCubeArray(m_Name);
            }
            else
            {
                material.m_Textures.InitTextureCube(m_Name);
            }
        }
        if (m_Type.m_Sampler.dim == glslang::TSamplerDim::Esd3D)
        {
            material.m_Textures.InitTexture3D(m_Name);
        }
        break;
    default:
        break;
    }
}

void GlslShader::Build()
{

    m_TShaders.clear();
    m_UniformVariables.clear();
    m_UniformBlocks.clear();

    glslang::InitializeProcess();

    glslang::TProgram program;

    for (const auto& it : m_ShaderStages)
    {
        std::shared_ptr<glslang::TShader> shader = std::make_shared<glslang::TShader>(it.m_Stage);
        m_TShaders.push_back(shader);

        const char* shaderCode = it.m_ShaderCode.c_str();
        shader->setStrings(&shaderCode, 1);
      
        if (!shader->parse(&DefaultTBuiltInResource, 100, false, EShMsgDefault)) {
            ztclog::info("Parse failed: %s ", shader->getInfoLog());
            return;
        }
        program.addShader(shader.get());

    }

    if (!program.link(EShMsgDefault)) {
        ztclog::info("Link failed: %s", program.getInfoLog());
        return;
    }
    program.buildReflection();

    glslang::TIntermediate* intermediate = program.getIntermediate(EShLangVertex);

    const int numUniforms = program.getNumUniformVariables();

    for (int i = 0; i < numUniforms; ++i) {

        UniformVariable inst;

        const auto var = program.getUniform(i);
        inst.m_Name = program.getUniformName(i);
        inst.m_Binding = var.getBinding();
        inst.m_LayoutLocation = var.layoutLocation();
        inst.m_Offset = var.offset;

        const auto type = var.getType();
        inst.m_Type = GlslShader::Type(type);
       
        if (inst.m_Type.m_Qualifier.hasUniformLayout())
        {
            ztclog::info("inst.m_Name %s has uniform layout", inst.m_Name.c_str());
        }
        //ztclog::info("_Name %s", inst.m_Name.c_str());

        if (IsMaterialVariable(inst.m_Name))
        {
            m_UniformVariables.push_back(inst);
        }
    }

    const auto& numUniformBlocks = program.getNumUniformBlocks();
    for (int i = 0; i < numUniformBlocks; ++i) {
        const auto& block = program.getUniformBlock(i);

        UniformBlock inst;
        inst.m_Binding = block.getBinding();
        inst.m_Name = block.name;
        inst.m_Index = block.index;
        inst.m_Size = block.size;
        inst.m_Type = GlslShader::Type(block.getType());
        m_UniformBlocks.push_back(inst);

    }
    glslang::FinalizeProcess();
}

void GlslShader::InfluenceMaterial(fxcc::graph::common::MaterialView& material) const
{
    for (const auto& uniformVariable : m_UniformVariables)
    {
        uniformVariable.Influence(material);
    }
}

bool fxcc::graph::common::GlslShader::IsMaterialVariable(const std::string& name)
{
    return name._Starts_with("material") && !name._Starts_with("material.pad");
}


 const TBuiltInResource GlslShader::DefaultTBuiltInResource = {
    /* .MaxLights = */ 32,
    /* .MaxClipPlanes = */ 6,
    /* .MaxTextureUnits = */ 32,
    /* .MaxTextureCoords = */ 32,
    /* .MaxVertexAttribs = */ 64,
    /* .MaxVertexUniformComponents = */ 4096,
    /* .MaxVaryingFloats = */ 64,
    /* .MaxVertexTextureImageUnits = */ 32,
    /* .MaxCombinedTextureImageUnits = */ 80,
    /* .MaxTextureImageUnits = */ 32,
    /* .MaxFragmentUniformComponents = */ 4096,
    /* .MaxDrawBuffers = */ 32,
    /* .MaxVertexUniformVectors = */ 128,
    /* .MaxVaryingVectors = */ 8,
    /* .MaxFragmentUniformVectors = */ 16,
    /* .MaxVertexOutputVectors = */ 16,
    /* .MaxFragmentInputVectors = */ 15,
    /* .MinProgramTexelOffset = */ -8,
    /* .MaxProgramTexelOffset = */ 7,
    /* .MaxClipDistances = */ 8,
    /* .MaxComputeWorkGroupCountX = */ 65535,
    /* .MaxComputeWorkGroupCountY = */ 65535,
    /* .MaxComputeWorkGroupCountZ = */ 65535,
    /* .MaxComputeWorkGroupSizeX = */ 1024,
    /* .MaxComputeWorkGroupSizeY = */ 1024,
    /* .MaxComputeWorkGroupSizeZ = */ 64,
    /* .MaxComputeUniformComponents = */ 1024,
    /* .MaxComputeTextureImageUnits = */ 16,
    /* .MaxComputeImageUniforms = */ 8,
    /* .MaxComputeAtomicCounters = */ 8,
    /* .MaxComputeAtomicCounterBuffers = */ 1,
    /* .MaxVaryingComponents = */ 60,
    /* .MaxVertexOutputComponents = */ 64,
    /* .MaxGeometryInputComponents = */ 64,
    /* .MaxGeometryOutputComponents = */ 128,
    /* .MaxFragmentInputComponents = */ 128,
    /* .MaxImageUnits = */ 8,
    /* .MaxCombinedImageUnitsAndFragmentOutputs = */ 8,
    /* .MaxCombinedShaderOutputResources = */ 8,
    /* .MaxImageSamples = */ 0,
    /* .MaxVertexImageUniforms = */ 0,
    /* .MaxTessControlImageUniforms = */ 0,
    /* .MaxTessEvaluationImageUniforms = */ 0,
    /* .MaxGeometryImageUniforms = */ 0,
    /* .MaxFragmentImageUniforms = */ 8,
    /* .MaxCombinedImageUniforms = */ 8,
    /* .MaxGeometryTextureImageUnits = */ 16,
    /* .MaxGeometryOutputVertices = */ 256,
    /* .MaxGeometryTotalOutputComponents = */ 1024,
    /* .MaxGeometryUniformComponents = */ 1024,
    /* .MaxGeometryVaryingComponents = */ 64,
    /* .MaxTessControlInputComponents = */ 128,
    /* .MaxTessControlOutputComponents = */ 128,
    /* .MaxTessControlTextureImageUnits = */ 16,
    /* .MaxTessControlUniformComponents = */ 1024,
    /* .MaxTessControlTotalOutputComponents = */ 4096,
    /* .MaxTessEvaluationInputComponents = */ 128,
    /* .MaxTessEvaluationOutputComponents = */ 128,
    /* .MaxTessEvaluationTextureImageUnits = */ 16,
    /* .MaxTessEvaluationUniformComponents = */ 1024,
    /* .MaxTessPatchComponents = */ 120,
    /* .MaxPatchVertices = */ 32,
    /* .MaxTessGenLevel = */ 64,
    /* .MaxViewports = */ 16,
    /* .MaxVertexAtomicCounters = */ 0,
    /* .MaxTessControlAtomicCounters = */ 0,
    /* .MaxTessEvaluationAtomicCounters = */ 0,
    /* .MaxGeometryAtomicCounters = */ 0,
    /* .MaxFragmentAtomicCounters = */ 8,
    /* .MaxCombinedAtomicCounters = */ 8,
    /* .MaxAtomicCounterBindings = */ 1,
    /* .MaxVertexAtomicCounterBuffers = */ 0,
    /* .MaxTessControlAtomicCounterBuffers = */ 0,
    /* .MaxTessEvaluationAtomicCounterBuffers = */ 0,
    /* .MaxGeometryAtomicCounterBuffers = */ 0,
    /* .MaxFragmentAtomicCounterBuffers = */ 1,
    /* .MaxCombinedAtomicCounterBuffers = */ 1,
    /* .MaxAtomicCounterBufferSize = */ 16384,
    /* .MaxTransformFeedbackBuffers = */ 4,
    /* .MaxTransformFeedbackInterleavedComponents = */ 64,
    /* .MaxCullDistances = */ 8,
    /* .MaxCombinedClipAndCullDistances = */ 8,
    /* .MaxSamples = */ 4,
    /* .maxMeshOutputVerticesNV = */ 256,
    /* .maxMeshOutputPrimitivesNV = */ 512,
    /* .maxMeshWorkGroupSizeX_NV = */ 32,
    /* .maxMeshWorkGroupSizeY_NV = */ 1,
    /* .maxMeshWorkGroupSizeZ_NV = */ 1,
    /* .maxTaskWorkGroupSizeX_NV = */ 32,
    /* .maxTaskWorkGroupSizeY_NV = */ 1,
    /* .maxTaskWorkGroupSizeZ_NV = */ 1,
    /* .maxMeshViewCountNV = */ 4,
    /* .maxMeshOutputVerticesEXT = */ 256,
    /* .maxMeshOutputPrimitivesEXT = */ 256,
    /* .maxMeshWorkGroupSizeX_EXT = */ 128,
    /* .maxMeshWorkGroupSizeY_EXT = */ 128,
    /* .maxMeshWorkGroupSizeZ_EXT = */ 128,
    /* .maxTaskWorkGroupSizeX_EXT = */ 128,
    /* .maxTaskWorkGroupSizeY_EXT = */ 128,
    /* .maxTaskWorkGroupSizeZ_EXT = */ 128,
    /* .maxMeshViewCountEXT = */ 4,
    /* .maxDualSourceDrawBuffersEXT = */ 1,

    /* .limits = */ {
        /* .nonInductiveForLoops = */ 1,
        /* .whileLoops = */ 1,
        /* .doWhileLoops = */ 1,
        /* .generalUniformIndexing = */ 1,
        /* .generalAttributeMatrixVectorIndexing = */ 1,
        /* .generalVaryingIndexing = */ 1,
        /* .generalSamplerIndexing = */ 1,
        /* .generalVariableIndexing = */ 1,
        /* .generalConstantMatrixVectorIndexing = */ 1,
    } };

